package socks

import (
	"errors"
	"fmt"
	"gitee.com/simple-set/simple.io/src/event"
	"gitee.com/simple-set/simple.io/src/process/codec"
	"net"
	"net/netip"
	"reflect"
)

// ResType  应答类型
type ResType byte

const (
	SuccessReply         ResType = 0 // 成功
	ServerFailure        ResType = 1 // 服务器异常
	RuleFailure          ResType = 2 // 规则不允许
	NetworkUnreachable   ResType = 3 // 网络无法访问
	HostUnreachable      ResType = 4 // 主机无法访问
	ConnectionRefused    ResType = 5 // 连接被拒绝
	TtlExpired           ResType = 6 // TTL超时
	CommandNotSupported  ResType = 7 // 不支持的命令
	AddrTypeNotSupported ResType = 8 // 不支持的地址类型
)

type Response struct {
	Version   Version        // 协议版本
	ResType   ResType        // 应答类型
	rsv       byte           // 保留地址
	AddType   AddrType       // 绑定地址类型
	BndAddr   []byte         // 绑定地址
	BndPort   uint16         // 绑定端口
	handshake *Handshake     // 握手对象
	buff      *codec.ByteBuf // io缓冲区
	serBytes  []byte         // 序列化数据
	session   *event.Session
}

func NewResponse(handshake *Handshake, session *event.Session) *Response {
	return &Response{
		Version:   V5,
		ResType:   SuccessReply,
		rsv:       0,
		AddType:   IpV4,
		BndAddr:   make([]byte, 0),
		handshake: handshake,
		buff:      handshake.buff,
		session:   session,
	}
}

func (r *Response) SetHandshake(handshake *Handshake) {
	r.handshake = handshake
	r.Version = handshake.version
}

func (r *Response) Reply(resType ResType) error {
	r.ResType = resType
	r.Serialize()
	return r.Encoder()
}

func (r *Response) Session() *event.Session {
	return r.session
}

// SetAddrAndPort 设置ip和端口
func (r *Response) SetAddrAndPort(addr net.Addr) error {
	var addrPort netip.AddrPort
	if tcpAddr, ok := addr.(*net.TCPAddr); ok {
		addrPort = tcpAddr.AddrPort()
	} else if udpAddr, ok := addr.(*net.UDPAddr); ok {
		addrPort = udpAddr.AddrPort()
	} else {
		return fmt.Errorf("invalid addr type: %s", reflect.TypeOf(addr).String())
	}

	if addrPort.Addr().Is4() {
		as4 := addrPort.Addr().As4()
		r.BndAddr = as4[:]
		r.AddType = IpV4
	}
	if addrPort.Addr().Is6() {
		as16 := addrPort.Addr().As16()
		r.BndAddr = as16[:]
		r.AddType = Ipv6
	}
	r.BndPort = addrPort.Port()
	return nil
}

// Encoder 编码器, 把Response对象转为为二进制写入缓冲区
func (r *Response) Encoder() error {
	if r.serBytes == nil {
		r.Serialize()
	}

	if _, err := r.buff.Write(r.serBytes); err != nil {
		return errors.New("response encode write failed: " + err.Error())
	}
	return r.buff.Flush()
}

func (r *Response) Serialize() []byte {
	r.serBytes = make([]byte, 0, 10)
	r.serBytes = append(r.serBytes, byte(r.Version))
	r.serBytes = append(r.serBytes, byte(r.ResType))
	r.serBytes = append(r.serBytes, byte(0))
	r.serBytes = append(r.serBytes, byte(r.AddType))
	if r.AddType == Domain {
		r.serBytes = append(r.serBytes, byte(len(r.BndAddr)))
	}
	r.serBytes = append(r.serBytes, r.BndAddr...)
	r.serBytes = append(r.serBytes, codec.Int16ToBytes(r.BndPort)...)
	return r.serBytes
}
